//=====================================================================
//
//  File:      ChangeSQLServerPassword.cpp
//  Summary:   This sample shows how to use OLE DB to change the password of a 
//             SQL Server Authentication user account.
//  Date:      November 20, 2007
//
//---------------------------------------------------------------------
//
//  This file is part of the Microsoft SQL Server Code Samples.
//  Copyright (C) Microsoft Corporation.  All rights reserved.
//
//This source code is intended only as a supplement to Microsoft
//Development Tools and/or on-line documentation.  See these other
//materials for detailed information regarding Microsoft code samples.
//
//THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF 
//ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO 
//THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
//PARTICULAR PURPOSE.
//
//===================================================================== 

void InitializeAndEstablishConnection();

#define STATUSDUMP(status)  case status : wprintf(L"status"); break;

#define UNICODE
#define DBINITCONSTANTS
#define INITGUID

#include <assert.h>
#include <windows.h>
#include <stdio.h>
#include <stddef.h>
#include <IOSTREAM>
#include <cguid.h>
#include <oledb.h>
#include <oledberr.h>

#include <SQLNCLI.h>

LPMALLOC pMalloc = NULL;
IDBInitialize * pIDBInitialize = NULL;
HRESULT hr;

void DumpErrorInfo (IUnknown* pObjectWithError, REFIID IID_InterfaceWithError);
void DumpErrorInfo (IUnknown* pObjectWithError, REFIID IID_InterfaceWithError, BOOL &has_sql_errors);

int main() {
   // All the initialization activities in a separate function.
   InitializeAndEstablishConnection();

   if ( FAILED( pIDBInitialize->Uninitialize() ) )
      // Uninitialize is not required, but fails if an interface was not released.
      printf("Problem uninitializing.\n");

   pIDBInitialize->Release();
   CoUninitialize();
};

void InitializeAndEstablishConnection() {
   IDBProperties * pIDBProperties = NULL;
   const ULONG nInitProps = 4;
   DBPROP InitProperties[nInitProps];
   const ULONG nSSInitProps = 1;
   DBPROP SSInitProperties[nSSInitProps];

   const ULONG nPropSet = 2;
   DBPROPSET rgInitPropSet[nPropSet];

   // Initialize the COM library.
   CoInitialize(NULL);
   CoGetMalloc(1, &pMalloc);

   CLSID clsid;
   CLSIDFromProgID(L"SQLNCLI10", &clsid);

   // Obtain access to the SQLOLEDB provider.
   hr = CoCreateInstance( clsid, NULL, CLSCTX_INPROC_SERVER, IID_IDBInitialize, (void **) &pIDBInitialize);
   if (FAILED(hr))
      printf("Failed in CoCreateInstance().\n");

   // Initialize the property values needed to establish the connection.
   for (int i = 0; i < nInitProps; i++)
      VariantInit(&InitProperties[i].vValue);

   // Specify database name.
   InitProperties[0].dwPropertyID = DBPROP_INIT_CATALOG;
   InitProperties[0].vValue.vt = VT_BSTR;
   // Change the following line to use any database on your server
   InitProperties[0].vValue.bstrVal = SysAllocString(L"master");
   InitProperties[0].dwOptions = DBPROPOPTIONS_REQUIRED;
   InitProperties[0].colid = DB_NULLID;

   InitProperties[1].dwPropertyID = DBPROP_AUTH_USERID;
   InitProperties[1].vValue.vt = VT_BSTR;
   // Modify the following line to add the user ID of a SQL Authentication account on your server
   InitProperties[1].vValue.bstrVal = SysAllocString(L"");
   InitProperties[1].dwOptions = DBPROPOPTIONS_REQUIRED;
   InitProperties[1].colid = DB_NULLID;

   InitProperties[2].dwPropertyID = DBPROP_AUTH_PASSWORD;
   InitProperties[2].vValue.vt = VT_BSTR;
   // Modify the following line to add the new SQL Authentication password
   InitProperties[2].vValue.bstrVal = SysAllocString(L"");
   InitProperties[2].dwOptions = DBPROPOPTIONS_REQUIRED;
   InitProperties[2].colid = DB_NULLID;

   InitProperties[3].dwPropertyID = DBPROP_INIT_DATASOURCE;
   InitProperties[3].vValue.vt = VT_BSTR;
   InitProperties[3].vValue.bstrVal = SysAllocString(L"(local)");
   InitProperties[3].dwOptions = DBPROPOPTIONS_REQUIRED;
   InitProperties[3].colid = DB_NULLID;

   SSInitProperties[0].dwPropertyID = SSPROP_AUTH_OLD_PASSWORD;
   SSInitProperties[0].vValue.vt = VT_BSTR;
   // Modify the following line to specify the existing SQL Authentication password
   SSInitProperties[0].vValue.bstrVal = SysAllocString(L"");
   SSInitProperties[0].dwOptions = DBPROPOPTIONS_REQUIRED;
   SSInitProperties[0].colid = DB_NULLID;

   rgInitPropSet[0].guidPropertySet = DBPROPSET_DBINIT;
   rgInitPropSet[0].cProperties = nInitProps;
   rgInitPropSet[0].rgProperties = InitProperties;

   rgInitPropSet[1].guidPropertySet = DBPROPSET_SQLSERVERDBINIT;
   rgInitPropSet[1].cProperties = nSSInitProps;
   rgInitPropSet[1].rgProperties = SSInitProperties;

   // Set initialization properties.
   hr = pIDBInitialize->QueryInterface( IID_IDBProperties, (void **)&pIDBProperties);

   if (FAILED(hr))
      printf("Failed to obtain IDBProperties interface.\n");

   hr = pIDBProperties->SetProperties( nPropSet, rgInitPropSet);

   if (FAILED(hr))
      printf("Failed to set initialization properties.\n");

   pIDBProperties->Release();

   // establish connection to the data source.
   DWORD now = GetTickCount();

   if ( FAILED(pIDBInitialize->Initialize()) ) {
      printf("Problem in initializing.\n");
      DumpErrorInfo(pIDBInitialize, IID_IDBInitialize);
   }

   DWORD tookms = GetTickCount() - now;
   printf("Connection took %d ms.\n", tookms);
}

// DumpErrorInfo queries SQLOLEDB error interfaces, retrieving available status
// or error information. This version is called when SQL Server errors are not expected.
void DumpErrorInfo (IUnknown* pObjectWithError, REFIID IID_InterfaceWithError) {
   BOOL has_sql_errors;
   DumpErrorInfo (pObjectWithError, IID_InterfaceWithError, has_sql_errors);
}

// DumpErrorInfo queries SQLOLEDB error interfaces, retrieving available status 
// or error information. This version is called when an SQL Server error could occur.
void DumpErrorInfo (IUnknown* pObjectWithError, REFIID IID_InterfaceWithError, BOOL &has_sql_errors ) {
   // Interfaces used in the example.
   IErrorInfo * pIErrorInfoAll = NULL;
   IErrorInfo * pIErrorInfoRecord = NULL;
   IErrorRecords * pIErrorRecords = NULL;
   ISupportErrorInfo * pISupportErrorInfo = NULL;
   ISQLErrorInfo * pISQLErrorInfo = NULL;
   ISQLServerErrorInfo * pISQLServerErrorInfo = NULL;

   // Number of error records.
   ULONG nRecs;
   ULONG nRec;

   // Basic error information from GetBasicErrorInfo.
   ERRORINFO errorinfo;

   // IErrorInfo values.
   BSTR bstrDescription;
   BSTR bstrSource;

   // ISQLErrorInfo parameters.
   BSTR bstrSQLSTATE;
   LONG lNativeError;

   // ISQLServerErrorInfo parameter pointers.
   SSERRORINFO * pSSErrorInfo = NULL;
   OLECHAR * pSSErrorStrings = NULL;

   // Hard-code an American English locale for the example.
   DWORD MYLOCALEID = 0x0409;

   has_sql_errors = 0;

   // Only ask for error information if the interface supports it.
   if (FAILED(pObjectWithError->QueryInterface(IID_ISupportErrorInfo, (void**) &pISupportErrorInfo)))   
      wprintf(L"SupportErrorErrorInfo interface not supported\n");
   else if (FAILED(pISupportErrorInfo->InterfaceSupportsErrorInfo(IID_InterfaceWithError)))   
      wprintf(L"InterfaceWithError interface not supported\n");


   // Do not test the return of GetErrorInfo. It can succeed and return
   // a NULL pointer in pIErrorInfoAll. Simply test the pointer.
   GetErrorInfo(0, &pIErrorInfoAll);

   // Test to see if it's a valid OLE DB IErrorInfo interface exposing a list of records.
   if (pIErrorInfoAll != NULL)  {
      if (SUCCEEDED(pIErrorInfoAll->QueryInterface(IID_IErrorRecords, (void**) &pIErrorRecords))) {
         pIErrorRecords->GetRecordCount(&nRecs);

         // Within each record, retrieve information from each of the defined interfaces.
         for (nRec = 0; nRec < nRecs; nRec++) {
            ULONG errorno = nRecs - nRec - 1;
            // From IErrorRecords, get the HRESULT and a reference to the ISQLErrorInfo interface.
            pIErrorRecords->GetBasicErrorInfo(errorno, &errorinfo);
            pIErrorRecords->GetCustomErrorObject(errorno,IID_ISQLErrorInfo, (IUnknown**) &pISQLErrorInfo);

            // Display the HRESULT, then use the ISQLErrorInfo.
            wprintf(L"HRESULT:\t%#X\n", errorinfo.hrError);

            if (pISQLErrorInfo != NULL) {
               pISQLErrorInfo->GetSQLInfo(&bstrSQLSTATE, &lNativeError);
               // Display SQLSTATE and native error values.
               wprintf(L"SQLSTATE:\t%s\nNative Error:\t%ld\n", bstrSQLSTATE, lNativeError);

               // SysFree BSTR references.
               SysFreeString(bstrSQLSTATE);

               // Get the ISQLServerErrorInfo interface from
               // ISQLErrorInfo before releasing the reference.
               pISQLErrorInfo->QueryInterface(IID_ISQLServerErrorInfo, (void**) &pISQLServerErrorInfo);

               pISQLErrorInfo->Release();
            }

            // Test to ensure the reference is valid, then get error information from ISQLServerErrorInfo.
            if (pISQLServerErrorInfo != NULL) {
               pISQLServerErrorInfo->GetErrorInfo(&pSSErrorInfo, &pSSErrorStrings);

               // ISQLServerErrorInfo::GetErrorInfo succeeds even when it
               // has nothing to return. Test the pointers before using.
               if (pSSErrorInfo) {
                  // Display the state and severity from the returned
                  // information. The error message comes from
                  // IErrorInfo::GetDescription.
                  wprintf(L"Error state:\t%d\nSeverity:\t%d\n", pSSErrorInfo->bState, pSSErrorInfo->bClass);
                  wprintf(L"Procedure:\t%s\nLine:\t%d\n", pSSErrorInfo->pwszProcedure, pSSErrorInfo->wLineNumber);

                  has_sql_errors++;

                  // IMalloc::Free needed to release references on returned values.
                  pMalloc->Free(pSSErrorStrings);
                  pMalloc->Free(pSSErrorInfo);
               }

               pISQLServerErrorInfo->Release();
            }

            if (SUCCEEDED(pIErrorRecords->GetErrorInfo(errno, MYLOCALEID, &pIErrorInfoRecord))) {
               // Get the source and description (error message) from the record's IErrorInfo.
               pIErrorInfoRecord->GetSource(&bstrSource);
               pIErrorInfoRecord->GetDescription(&bstrDescription);

               if (bstrSource != NULL) {
                  wprintf(L"Source:\t\t%s\n", bstrSource);
                  SysFreeString(bstrSource);
               }

               if (bstrDescription != NULL) {
                  wprintf(L"Error message:\t%s\n", bstrDescription);
                  SysFreeString(bstrDescription);
               }

               pIErrorInfoRecord->Release();
            }
         }

         pIErrorRecords->Release();
      }
      else {
         // IErrorInfo is valid; get the source and
         // description to see what it is.
         pIErrorInfoAll->GetSource(&bstrSource);
         pIErrorInfoAll->GetDescription(&bstrDescription);

         if (bstrSource != NULL) {
            wprintf(L"Source:\t\t%s\n", bstrSource);
            SysFreeString(bstrSource);
         }

         if (bstrDescription != NULL) {
            wprintf(L"Error message:\t%s\n", bstrDescription);
            SysFreeString(bstrDescription);
         }
      }

      pIErrorInfoAll->Release();
   }
   else
      wprintf(L"GetErrorInfo failed.\n");

   if (pISupportErrorInfo != NULL)
      pISupportErrorInfo->Release();
}

